In this tutorial, you will configure a Linux server to send Email through Gmail. It’s much easier than setting up a full-featured Email server and is more appropriate if all you need is to get Email alerts out from your server or application.
I’ve also included a quick script at the end of this tutorial that will check the status of systemd periodically and send out Email alerts if any problems are detected.
This guide assumes you are using Debian/Ubuntu, some commands and package names may need to be adapted to your OS if you are not.
In this example I am using Gmail because it is a popular choice, other mail services will work in a similar manner. You will need to enable the “less secure apps” setting in Gmail to allow your server to log in and send mail without getting blocked by Google’s security measures, visit https://myaccount.google.com/lesssecureapps, make sure you have the correct account selected in the top right corner, and then turn the feature on.
We will install the following packages:
•.postfix: A mail transfer agent
•.mailutils: A set of command-line utilities for working with Email, most notably the mail command that is useful for sending an Email from command-line.
•.libsasl2-modules: A module used by postfix to authenticate to an external Email server like Gmail with a username and password.
apt install -y postfix mailutils libsasl2-modules
For CentOS, use yum install -y postfix mailx cyrus-sasl-plugin
Postfix will need to log in through Gmail to submit messages, you’ll need to create the sasl_passwd file and add your credentials to it.
[smtp.gmail.com]:587 username@gmail.com:password
The postmap command will convert our text file into a lookup table, which is a data format that postfix can quickly and efficiently parse for data.
postmap /etc/postfix/sasl_passwd
Since these two files contain your Gmail password, it is wise to lock down their permissions so only the root user can access their contents.
chown root. /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
chown root. /etc/postfix/sasl_passwd.db
chmod 600 /etc/postfix/sasl_passwd.db
the chown command changes the owner user and group, and the chmod command will changes the access mode (permissions), more detailed information on ownership and permissions can be found here.
Using chown root. is the same as chown root:root or chown root.root, leaving the group name blank will use the default group for the user specified.
We need to set up aliases and virtual alias maps, aliases will redirect mail from one user to another on the same host, for example from postmaster@ServerHostName to root@ServerHostName, we will direct all Email to root here. Virtual alias maps complete the picture by redirecting mail from local users on this host to an external Email address.
How to Home Lab: Part 5 - Secure SSH Remote Access
You should run the command newaliases after changing this file so it will be parsed appropriately. You can add any other local users that may receive mail to this list.
postmaster: root
nobody: root
hostmaster: root
webmaster: root
www: root
The Email address here is where any mail will be directed.
root you@yourdomain.com
The virtual alias map file also needs to be post mapped.
postmap /etc/postfix/virtual
In the main configuration file for postfix, update the values for the following lines, if a line is missing from your configuration file you’ll just need to add the whole line.
These settings tell postfix to use Gmail as a relay to send mail out, and then specify the parameters and credentials for connecting to Gmail.
relayhost = [smtp.gmail.com]:587
smtp_tls_security_level = may
smtp_sasl_auth_enable = yes
smtp_sasl_security_options =
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
These settings are for aliasing as explained above
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
virtual_alias_maps = hash:/etc/postfix/virtual
I also remove all the values from mydestination, this tells postfix it is not to receive any mail on this system, it will only send it out the relay host or reject the message.
mydestination =
If you do not have IPv6 enabled, you will need to specify the following value in main.cf as well, often DNS queries for Gmail will return the IPv6 address first, and postfix will fail to send mail because the relay host is unreachable.
inet_protocols = ipv4
Restart postfix to apply the configuration.
systemctl restart postfix
Now you should be able to send a test Email with the command below, we will send the email to the root user and it should get re-routed to the final address specified in /etc/postfix/virtual.
echo "This is a test Email" | mail -s "Testing" root
Bring up the logs for postfix with the command below, then hold the shift key and press the letter g on your keyboard to jump to the bottom where the newest entries are.
less /var/log/mail.log
If everything went smoothly, you should see log entries similar to those below.
postfix/pickup[110202]: 4317C18E47: uid=0 from=<root>
postfix/cleanup[62865]: 4317C18E47: message-id=<20190805104206.4317C18E47@ServerHostName>
postfix/qmgr[5024]: 4317C18E47: from=<root@ServerHostName>, size=249104, nrcpt=1 (queue active)
postfix/smtp[62876]: 4317C18E47: to=<you@yourdomain.com>, relay=smtp.gmail.com[74.125.195.109]:587, delay=1.3, delays=0.04/0.01/0.02/1.2, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 5B6D12E007B)
postfix/qmgr[5024]: 4317C18E47: removed
If you are setting this up on a VPS, you may see an error message like the one below. In which case you just need to log in to the Gmail account from your web browser, you should see an alert there stating that a login attempt was blocked; click through and choose “This was me”, and test sending mail again after that.
postfix/smtp[19724]: E3C71A4D: to=<you@yourdomain.com>, orig_to=<root>, relay=smtp.gmail.com[108.177.9.108]:587, delay=202, delays=202/0.05/0.31/0, dsn=4.7.14, status=deferred (SASL authentication failed; server smtp.gmail.com[108.177.9.108] said: 534-5.7.14 <https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=AKgnsbv6?534-5.7.14 Y_y6_l6lnq7awbD3g-kRUqe5uT5hbCPVkxC3L57wTzokPWquZVVw4B9Hk9dapPHmFWHhp5?534-5.7.14 9UOBPafiJZBzXH36l0MyenRtgeMtrMdv1Wxt1W2wEepiy1iB6thU3uuYsWqBzI> Please?534-5.7.14 log in via your web browser and then try again.?534-5.7.14 Learn more at?534 5.7.14 https://support.google.com/mail/answer/78754 q24sm27754378otl.31 - gsmtp)
How to Home Lab: Part 7 - Log Management
This script will check the status of systemd, if all is well it just quits, if not it will fire off an Email alert specifying which unit has failed, and re-check every minute until the issue is resolved (without sending additional Emails until all services are restored).
Further Reading: The book Linux Command Line and Shell Scripting Bible, by Richard Blum, is a fantastic reference when learning how to write bash shell scripts because it is very easy to follow. Available on Amazon.
First, we need to create a file for the script and make it executable.
touch /usr/local/bin/systemd-failed-notifier.sh
chmod +x /usr/local/bin/systemd-failed-notifier.sh
Copy and paste the script below into that file.
#!/bin/bash
emailAddress="root"
statusFile="/tmp/systemd-failed-notifier-status"
lockFile="/tmp/systemd-failed-notifier-lock"
# Check for lock file and create one or quit
if [ -f "$lockFile" ]; then
exit 0
else
touch "$lockFile"
fi
# Read status file if it exists or create it
if [ -f "$statusFile" ]; then
source "$statusFile"
else
touch "$statusFile"
lastSystemStatus="unknown"
fi
doAlert () {
# If system status has changed, update statusFile and send email
if [ ! "$systemStatus" = "$lastSystemStatus" ]; then
if [ "$systemStatus" = "degraded" ]; then
failedUnits="$(systemctl --failed | grep failed | cut -f2 -d' ')"
else
failedUnits=""
fi
echo -e \
"
Current Status: $systemStatus
Previous Status: $lastSystemStatus
Failed Units:
$failedUnits" | \
mail -s "$(hostname) $systemStatus" $emailAddress
sleep 5
lastSystemStatus="$systemStatus"
fi
}
# Run once
systemStatus=$(systemctl is-system-running)
doAlert
# Loop if system status is not running
while [ ! "$systemStatus" = "running" ]; do
sleep 60
systemStatus=$(systemctl is-system-running)
doAlert
done
# Cleanup and exit
echo lastSystemStatus="$systemStatus" > "$statusFile"
rm "$lockFile"
exit 0
We just need to create the service and timer files for systemd to run the script periodically.
touch /etc/systemd/system/systemd-failed-notifier.service
touch /etc/systemd/system/systemd-failed-notifier.timer
[Unit]
Description= Systemd status email alert service
[Service]
Type=simple
ExecStart=/usr/local/bin/systemd-failed-notifier.sh
The value OnBootSec sets the time to wait after the system boots up before running the script for the first time, and OnUnitActiveSec sets the time to wait in between each subsequent run of the script after that.
[Unit]
Description=Systemd status email alert timer
[Timer]
OnBootSec=5min
OnUnitActiveSec=60min
Unit=systemd-failed-notifier.service
[Install]
WantedBy=timers.target
The following commands will configure the system to start the timer every time it boots up, and to start the timer right now.
systemctl enable systemd-failed-notifier.timer
systemctl start systemd-failed-notifier.timer